/*
 * Allwinner SoCs display driver.
 *
 * Copyright (C) 2016 Allwinner.
 *
 * This file is licensed under the terms of the GNU General Public
 * License version 2.  This program is licensed "as is" without any
 * warranty of any kind, whether express or implied.
 */

/**
 *	All Winner Tech, All Right Reserved. 2014-2015 Copyright (c)
 *
 *	File name   :       de2_wb_ebios.c
 *
 *	Description :       display engine 2.0 WBC basic function definition
 *
 *	History     :       2014/03/03   wangxuan   initial version
 *	                    2014/04/02   wangxuan   change the register
 *	                                            operation from bits to word
 */
#include "de_wb_type.h"
#include "de_wb.h"
#define WB_MODULE_NUMBER DE_NUM
#define WB_OFFSET 0x010000

static volatile struct __wb_reg_t *wb_dev[WB_MODULE_NUMBER] = { NULL };
static struct disp_capture_config wb_config;

unsigned int wb_lan2coefftab16[16] = {
	0x00004000, 0x00033ffe, 0x00063efc, 0x000a3bfb,
	0xff0f37fb, 0xfe1433fb, 0xfd192ffb, 0xfd1f29fb,
	0xfc2424fc, 0xfb291ffd, 0xfb2f19fd, 0xfb3314fe,
	0xfb370fff, 0xfb3b0a00, 0xfc3e0600, 0xfe3f0300
};

unsigned int wb_lan2coefftab16_down[16] = {
	0x000e240e, 0x0010240c, 0x0013230a, 0x00142309,
	0x00162208, 0x01182106, 0x011a2005, 0x021b1f04,
	0x031d1d03, 0x041e1c02, 0x05201a01, 0x06211801,
	0x07221601, 0x09231400, 0x0a231300, 0x0c231100
};

int rgb2yuv601[3][4] = {
	{0x00bb, 0x0274, 0x003f, 0x04200},
	{0x1f98, 0x1ea5, 0x01c1, 0x20200},
	{0x01c1, 0x1e67, 0x1fd7, 0x20200} };/* rgb2yuv */
int rgb2yuv709[3][4] = {
	{0x00da, 0x02dc, 0x0049, 0x00200},
	{0xff8b, 0xfe76, 0x0200, 0x20200},
	{0x0200, 0xfe30, 0xffd1, 0x20200} };/* rgb2yuv */
int r2gray[3][4] = {
	{0x0155, 0x0155, 0x0156, 0x0200},
	{0x0000, 0x0000, 0x0000, 0x0000},
	{0x0000, 0x0000, 0x0000, 0x0000} };/* rgb2gray */


/**
 *	function       : wb_ebios_set_reg_base(u32 sel, u32 base)
 *	description    : setup write-back controller register base
 *	parameters     :
 *	                     sel <controller select>
 *	                     base <register base>
 *	return         :
 *	                 success
 */
s32 wb_ebios_set_reg_base(u32 sel, uintptr_t base)
{
#if defined(CONFIG_INDEPENDENT_DE)
	if (sel >= 0 && sel < DE_NUM)
		wb_dev[sel] = (struct __wb_reg_t *) (base + WB_OFFSET);
#else
	int i = 0;

	__inf("sel=%d, base=0x%p\n", sel, (void *)(base + WB_OFFSET));

	for (i = 0; i < DE_NUM; ++i)
		wb_dev[i] = (struct __wb_reg_t *) (base + WB_OFFSET);

#endif
	return 0;
}

/**
 *	function       : wb_ebios_get_reg_base(u32 sel)
 *	description    : get write-back controller register base
 *	parameters     :
 *	                     sel <controller select>
 *	return         :
 *	                 registers base
 */
uintptr_t wb_ebios_get_reg_base(u32 sel)
{
	uintptr_t ret = 0;

	ret = (uintptr_t) wb_dev[sel];

	return ret;
}

/**
 *	function       : wb_ebios_init(struct disp_bsp_init_para *para)
 *	description    : initial write-back controller registers
 *	parameters     :
 *	                     para <>
 *	return         :
 *	                 success
 */
s32 wb_ebios_init(struct disp_bsp_init_para *para)
{
	wb_ebios_set_reg_base(0, para->reg_base[DISP_MOD_DE]);
#if defined(CONFIG_INDEPENDENT_DE)
	wb_ebios_set_reg_base(1, para->reg_base[DISP_MOD_DE1]);
#endif
	/* FIXME, need */
	/* memset((void*)wb_dev[0], 0, sizeof(struct __wb_reg_t)); */
	return 0;
}

/**
 *	function       : wb_ebios_exit
 *	description    : de-initial write-back controller registers
 *	parameters     :
 *	                     para <>
 *	return         :
 *	                 success
 */
s32 wb_ebios_exit(void)
{
	return 0;
}

/**
 *	function       : wb_ebios_writeback_enable(u32 sel, bool en)
 *	description    : enable write-back once
 *	parameters     :
 *	                     sel <controller select>
 *	                     en <0:disable; 1:enable>
 *	return         :
 *	                 success
 */
s32 wb_ebios_writeback_enable(u32 sel, bool en)
{
	wb_dev[sel]->gctrl.dwval |= ((en == 0) ? 0x0 : 0x00000001);

	return 0;
}

/**
 *	function       : wb_ebios_apply(u32 sel,
 *					struct disp_capture_config *cfg)
 *	description    : save user config
 *	parameters     :
 *	                     sel <controller select>
 *	                     cfg <write-back information,include input_fb and
 *	                          output_fb information>
 *	return         :
 *	                 0   success
 *	                 -1  fail
 *	note           : Don't support YUV input yet 14-02-28
 *	                 when write-back format is yuv, default 16-235 output
 */
s32 wb_ebios_apply(u32 sel, struct disp_capture_config *cfg)
{
	memcpy(&wb_config, cfg, sizeof(struct disp_capture_config));
	return 0;
}

/**
 *	function       : wb_ebios_update_regs(u32 sel)
 *	description    : transform user config into hw config
 *	parameters     :
 *	                     sel <controller select>
 *	return         :
 *	                 0   success
 *	                 -1  fail
 */
s32 wb_ebios_update_regs(u32 sel)
{
	wb_ebios_set_para(sel, &wb_config);
	return 0;
}

/**
 *	function       : wb_ebios_set_para(u32 sel,disp_capture_config *cfg)
 *	description    : setup write-back controller parameters
 *	parameters     :
 *	                     sel <controller select>
 *	                     cfg <write-back information,include input_fb and
 *	                          output_fb information>
 *	return         :
 *	                 0   success
 *	                 -1  fail
 *	note           : Don't support YUV input yet 14-02-28
 *	                 when write-back format is yuv, default 16-235 output
 */
s32 wb_ebios_set_para(u32 sel, struct disp_capture_config *cfg)
{
	u32 in_port;
	u32 in_w, in_h;
	u32 crop_x, crop_y, crop_w, crop_h;
	u32 out_addr[3];
	u32 out_buf_w, out_buf_h;
	u32 out_fmt;
	u32 out_window_w, out_window_h, out_window_x, out_window_y;
	u32 cs_out_w0 = 0, cs_out_h0 = 0, cs_out_w1 = 0, cs_out_h1 = 0;
	u32 fs_out_w0, fs_out_h0, fs_out_w1, fs_out_h1;
	u32 step_h, step_v;
	u32 v_intg, v_frac, h_intg, h_frac;
	u32 down_scale_y, down_scale_c;
	u32 i;
	u32 bypass_val = 0;
	/* get para */
	in_port = cfg->disp;
	in_w = cfg->in_frame.size[0].width;
	in_h = cfg->in_frame.size[0].height;
	crop_x = (u32) cfg->in_frame.crop.x;
	crop_y = (u32) cfg->in_frame.crop.y;
	crop_w = cfg->in_frame.crop.width;
	crop_h = cfg->in_frame.crop.height;
	out_addr[0] = cfg->out_frame.addr[0];
	out_addr[1] = cfg->out_frame.addr[1];
	out_addr[2] = cfg->out_frame.addr[2];
	out_buf_w = cfg->out_frame.size[0].width;
	out_buf_h = cfg->out_frame.size[0].height;
	out_fmt = cfg->out_frame.format;
	out_window_x = cfg->out_frame.crop.x;
	out_window_y = cfg->out_frame.crop.y;
	out_window_w = cfg->out_frame.crop.width;
	out_window_h = cfg->out_frame.crop.height;
	if (in_port >= DE_NUM)
		return -1;
	if ((in_w < MININWIDTH) || (in_w > MAXINWIDTH) || (in_h < MININHEIGHT)
	    || (in_h > MAXINHEIGHT))
		return -1;
	if ((crop_w < MININWIDTH) || (crop_w > MAXINWIDTH)
	    || (crop_h < MININHEIGHT) || (crop_h > MAXINHEIGHT))
		return -1;
	if ((crop_x + crop_w > in_w) || (crop_y + crop_h > in_h))
		return -1;
	if ((out_buf_w < out_window_w) || (out_buf_h < out_window_h))
		return -1;
	if ((out_window_w < MININWIDTH) || (out_window_w > MAXINWIDTH)
	    || (out_window_h < MININHEIGHT) || (out_window_h > MAXINHEIGHT))
		return -1;
	if ((out_window_w > crop_w) || (out_window_h > crop_h))
		return -1;
	if (((out_fmt == DISP_FORMAT_YUV420_P)
	     || (out_fmt == DISP_FORMAT_YUV420_SP_VUVU)
	     || (out_fmt == DISP_FORMAT_YUV420_SP_UVUV))
	    && (out_window_w > LINE_BUF_LEN))
		return -1;
	/* gctrl */
	wb_dev[sel]->gctrl.dwval |= (0x20000000 | (in_port << 16));
	/* input size */
	wb_dev[sel]->size.dwval = (in_w - 1) | ((in_h - 1) << 16);
	/* input crop window */
	wb_dev[sel]->crop_coord.dwval = crop_x | ((crop_y) << 16);
	wb_dev[sel]->crop_size.dwval = (crop_w - 1) | ((crop_h - 1) << 16);
	/* output fmt */
	if (out_fmt == DISP_FORMAT_ARGB_8888)
		wb_dev[sel]->fmt.dwval = WB_FORMAT_ARGB_8888;
	else if (out_fmt == DISP_FORMAT_ABGR_8888)
		wb_dev[sel]->fmt.dwval = WB_FORMAT_ABGR_8888;
	else if (out_fmt == DISP_FORMAT_RGBA_8888)
		wb_dev[sel]->fmt.dwval = WB_FORMAT_RGBA_8888;
	else if (out_fmt == DISP_FORMAT_BGRA_8888)
		wb_dev[sel]->fmt.dwval = WB_FORMAT_BGRA_8888;
	else if (out_fmt == DISP_FORMAT_RGB_888)
		wb_dev[sel]->fmt.dwval = WB_FORMAT_RGB_888;
	else if (out_fmt == DISP_FORMAT_BGR_888)
		wb_dev[sel]->fmt.dwval = WB_FORMAT_BGR_888;
	else if (out_fmt == DISP_FORMAT_YUV420_P)
		wb_dev[sel]->fmt.dwval = WB_FORMAT_YUV420_P;
	else if (out_fmt == DISP_FORMAT_YUV420_SP_VUVU)
		wb_dev[sel]->fmt.dwval = WB_FORMAT_YUV420_SP_UVUV;
	else if (out_fmt == DISP_FORMAT_YUV420_SP_UVUV)
		wb_dev[sel]->fmt.dwval = WB_FORMAT_YUV420_SP_VUVU;
	else
		return -1;

	/* output addr and pitch for different fmt */
	if ((out_fmt == DISP_FORMAT_ARGB_8888)
	    || (out_fmt == DISP_FORMAT_ABGR_8888)
	    || (out_fmt == DISP_FORMAT_RGBA_8888)
	    || (out_fmt == DISP_FORMAT_BGRA_8888)) {
		out_addr[0] += (out_window_y * out_buf_w + out_window_x) << 2;
		wb_dev[sel]->wb_addr_a0.dwval = out_addr[0];
		wb_dev[sel]->wb_pitch0.dwval = out_buf_w << 2;
	}
	if ((out_fmt == DISP_FORMAT_RGB_888)
	    || (out_fmt == DISP_FORMAT_BGR_888)) {
		out_addr[0] += (out_window_y * out_buf_w + out_window_x) * 3;
		wb_dev[sel]->wb_addr_a0.dwval = out_addr[0];
		wb_dev[sel]->wb_pitch0.dwval = 3 * out_buf_w;
	}
	if ((out_fmt == DISP_FORMAT_YUV420_SP_VUVU)
	    || (out_fmt == DISP_FORMAT_YUV420_SP_UVUV)) {
		out_addr[0] += (out_window_y * out_buf_w + out_window_x);
		out_addr[1] +=
		    (((out_window_y * out_buf_w) >> 1) + out_window_x);
		wb_dev[sel]->wb_addr_a0.dwval = out_addr[0];
		wb_dev[sel]->wb_addr_a1.dwval = out_addr[1];
		wb_dev[sel]->wb_pitch0.dwval = out_buf_w;
		wb_dev[sel]->wb_pitch1.dwval = out_buf_w;
	}
	if (out_fmt == DISP_FORMAT_YUV420_P) {
		out_addr[0] += (out_window_y * out_buf_w + out_window_x);
		out_addr[1] += (out_window_y * out_buf_w + out_window_x) >> 1;
		out_addr[2] += (out_window_y * out_buf_w + out_window_x) >> 1;
		wb_dev[sel]->wb_addr_a0.dwval = out_addr[0];
		wb_dev[sel]->wb_addr_a1.dwval = out_addr[1];
		wb_dev[sel]->wb_addr_a2.dwval = out_addr[2];
		wb_dev[sel]->wb_pitch0.dwval = out_buf_w;
		wb_dev[sel]->wb_pitch1.dwval = out_buf_w >> 1;
	}
	/* CSC */
	if ((out_fmt == DISP_FORMAT_ARGB_8888)
	    || (out_fmt == DISP_FORMAT_ABGR_8888)
	    || (out_fmt == DISP_FORMAT_RGBA_8888)
	    || (out_fmt == DISP_FORMAT_BGRA_8888)
	    || (out_fmt == DISP_FORMAT_RGB_888)
	    || (out_fmt == DISP_FORMAT_BGR_888)) {
		bypass_val &= 0xfffffffe;
	 } else {
		bypass_val |= 0x00000001;
	}
	/* Coarse scaling */
	if ((crop_w > (out_window_w << 1)) && (crop_h > (out_window_h << 1))) {
		bypass_val |= 0x00000002;
		wb_dev[sel]->cs_horz.dwval = crop_w | (out_window_w << 17);
		wb_dev[sel]->cs_vert.dwval = crop_h | (out_window_h << 17);
		cs_out_w0 = out_window_w << 1;
		cs_out_h0 = out_window_h << 1;
	}
	if ((crop_w > (out_window_w << 1)) && (crop_h <= (out_window_h << 1))) {
		bypass_val |= 0x00000002;
		wb_dev[sel]->cs_horz.dwval = crop_w | (out_window_w << 17);
		wb_dev[sel]->cs_vert.dwval = 0;
		cs_out_w0 = out_window_w << 1;
		cs_out_h0 = crop_h;
	}
	if ((crop_w <= (out_window_w << 1)) && (crop_h > (out_window_h << 1))) {
		bypass_val |= 0x00000002;
		wb_dev[sel]->cs_horz.dwval = 0;
		wb_dev[sel]->cs_vert.dwval = crop_h | (out_window_h << 17);
		cs_out_w0 = crop_w;
		cs_out_h0 = out_window_h << 1;
	}
	if ((crop_w <= (out_window_w << 1))
		&& (crop_h <= (out_window_h << 1))) {
		bypass_val &= 0xfffffffd;
		wb_dev[sel]->cs_horz.dwval = 0;
		wb_dev[sel]->cs_vert.dwval = 0;
		cs_out_w0 = crop_w;
		cs_out_h0 = crop_h;
	}
	/* Fine scaling */
	cs_out_w1 = cs_out_w0;
	cs_out_h1 = cs_out_h0;
	fs_out_w0 = out_window_w;
	fs_out_w1 =
	    ((out_fmt == DISP_FORMAT_YUV420_P)
	    | (out_fmt == DISP_FORMAT_YUV420_SP_VUVU)
	    | (out_fmt == DISP_FORMAT_YUV420_SP_UVUV)) ?
	    (out_window_w >> 1) : out_window_w;
	fs_out_h0 = out_window_h;
	fs_out_h1 =
	    ((out_fmt == DISP_FORMAT_YUV420_P)
	    | (out_fmt == DISP_FORMAT_YUV420_SP_VUVU)
	    | (out_fmt == DISP_FORMAT_YUV420_SP_UVUV)) ?
	    (out_window_h >> 1) : out_window_h;
	if ((cs_out_w0 == fs_out_w0) && (cs_out_h0 == fs_out_h0)
	    && (cs_out_w1 == fs_out_w1) && (cs_out_h1 == fs_out_h1)) {
		bypass_val &= 0xfffffffb;
		wb_dev[sel]->fs_hstep.dwval = 1 << 20;
		wb_dev[sel]->fs_vstep.dwval = 1 << 20;
	} else {
		unsigned long long tmp;

		bypass_val |= 0x00000004;
		tmp = ((long long)cs_out_w0 << LOCFRACBIT);
		do_div(tmp, (long long)out_window_w);
		step_h = (int)tmp;
		tmp = ((long long)cs_out_h0 << LOCFRACBIT);
		do_div(tmp, (long long)out_window_h);
		step_v = (int)tmp;
		h_intg = (step_h & (~((1 << LOCFRACBIT) - 1))) >> LOCFRACBIT;
		h_frac = step_h & ((1 << LOCFRACBIT) - 1);
		v_intg = (step_v & (~((1 << LOCFRACBIT) - 1))) >> LOCFRACBIT;
		v_frac = step_v & ((1 << LOCFRACBIT) - 1);
		wb_dev[sel]->fs_hstep.dwval = (h_frac << 2) | (h_intg << 20);
		wb_dev[sel]->fs_vstep.dwval = (v_frac << 2) | (v_intg << 20);
		if (cs_out_w0 <= fs_out_w0)
			down_scale_y = 0;
		else
			down_scale_y = 1;
		if (cs_out_w1 <= fs_out_w1)
			down_scale_c = 0;
		else
			down_scale_c = 1;
		for (i = 0; i < SCALERPHASE; i++) {
			wb_dev[sel]->yhcoeff[i].dwval =
			    down_scale_y ? wb_lan2coefftab16_down[i] :
			    wb_lan2coefftab16[i];
			wb_dev[sel]->chcoeff[i].dwval =
			    down_scale_c ? wb_lan2coefftab16_down[i] :
			    wb_lan2coefftab16[i];
		}
	}
	wb_dev[sel]->fs_insize.dwval =
	    (cs_out_w0 - 1) | ((cs_out_h0 - 1) << 16);
	wb_dev[sel]->fs_outsize.dwval =
	    (out_window_w - 1) | ((out_window_h - 1) << 16);
	wb_dev[sel]->bypass.dwval = bypass_val;
	return 0;
}

/**
 *	function       : wb_ebios_get_status(u32 sel)
 *	description    : get last frame write back status
 *	parameters     :
 *	                     sel <controller select>
 *	return         :
 *	                 0    writeback finish
 *	                 1    overflow
 *	                 2    timeout
 *	                 3    not start
 */
u32 wb_ebios_get_status(u32 sel)
{
	u32 status;

	status = wb_dev[sel]->status.dwval & 0x71;
	wb_dev[sel]->status.dwval = 0x71;

	if (status & 0x10)
		return 0;
	else if (status & 0x20)
		return 1;
	else if (status & 0x40)
		return 2;
	else
		return 3;
}

/* INTERRUPT */
s32 wb_ebios_enableint(u32 sel)
{
	wb_dev[sel]->intr.dwval |= 0x00000001;
	return 0;
}

s32 wb_ebios_disableint(u32 sel)
{
	wb_dev[sel]->intr.dwval &= 0xfffffffe;
	return 0;
}

u32 wb_ebios_queryint(u32 sel)
{
	return wb_dev[sel]->status.dwval;
}

u32 wb_ebios_clearint(u32 sel)
{
	/* write 1 to clear */
	wb_dev[sel]->status.dwval |=
	    (WB_END_IE | WB_FINISH_IE | WB_FIFO_OVERFLOW_ERROR_IE |
	     WB_TIMEOUT_ERROR_IE);
	return 0;
}

/**
 * @name       :wb_input_select
 * @brief      :rt-wb input port select
 * @param[IN]  :sel:port index(0~3)
 * @return     :0 if success, -1 else
 */
s32 wb_input_select(u32 sel)
{
	if (sel >= DE_NUM)
		return -1;

	wb_dev[sel]->gctrl.bits.in_port_sel = sel;
	return 0;
}

#if defined(CONFIG_EINK_PANEL_USED)
/* ************************************************************************* */
/* function       : WB_eink_Set_Reg_Base(unsigned int sel, unsigned int base) */
/* description    : setup write-back controller register base */
/* parameters     : */
/* sel <controller select> */
/* base <register base> */
/* return         : */
/* success */
/* ************************************************************************* */
int wb_eink_set_reg_base(unsigned int sel, unsigned int base)
{
	__inf("sel=%d, base=0x%x\n", sel, base + WB_OFFSET);
	wb_dev[sel] = (struct __wb_reg_t *)(base + WB_OFFSET);

	return 0;
}

/* ************************************************************************* */
/* function       : WB_eink_Get_Reg_Base(unsigned int sel) */
/* description    : get write-back controller register base */
/* parameters     : */
/* sel <controller select> */
/*  */
/* return         : */
/* registers base */
/* ************************************************************************* */
unsigned int wb_eink_get_reg_base(unsigned int sel)
{
	unsigned int ret = 0;

	ret = (unsigned int)wb_dev[sel];

	return ret;
}

/* ************************************************************************* */
/* function       : WB_eink_Init(unsigned int sel) */
/* description    : initial write-back controller registers */
/* parameters     : */
/* sel <controller select> */
/*  */
/* return         : */
/* success */
/* ************************************************************************* */
int wb_eink_init(unsigned int sel, unsigned int base_addr)
{
	wb_eink_set_reg_base(sel, base_addr); /* 0x01000000 */
	return 0;
}

int wb_eink_close(unsigned int sel)
{
	wb_dev[sel]->gctrl.dwval = 0;
	return 0;
}

/* ************************************************************************* */
/* function       : WB_eink_Writeback_Enable(unsigned int sel) */
/* description    : enable write-back once */
/* parameters     : */
/* sel <controller select>, en<0:disable; 1:enable> */
/* return         : */
/* success */
/* ************************************************************************* */
int wb_eink_writeback_enable(unsigned int sel)
{
	wb_dev[sel]->gctrl.dwval |= 1;

	return 0;
}

int wb_eink_reset(unsigned int sel)
{
	wb_dev[sel]->gctrl.dwval = 0x20000010;

	return 0;
}

int wb_eink_dereset(unsigned int sel)
{
	wb_dev[sel]->gctrl.dwval = 0x00000000;

	return 0;
}
/* ************************************************************************* */
/* function: WB_eink_Set_Para(unsigned int sel,disp_capture_config *cfg) */
/* description: setup write-back controller parameters */
/* parameters : */
/* sel <controller select> */
/* wb_config <write-back information,*/
/*include input_fb and output_fb information> */
/* return: */
/* 0	--	success */
/* -1	--	fail */
/* note		  : Don't support YUV input yet 14-02-28 */
/* when write-back format is yuv, default 16-235 output */
/* ************************************************************************* */
int wb_eink_set_para(unsigned int sel, __einkwb_config_t *cfg)
{
	unsigned int in_port;
	unsigned int in_w, in_h;
	unsigned int crop_x, crop_y, crop_w, crop_h;
	unsigned int out_addr[3];
	unsigned int out_buf_w, out_buf_h;
	unsigned int out_fmt;
	unsigned int out_window_w, out_window_h, out_window_x, out_window_y;
	unsigned int csc_std;
	unsigned int self_sync;
	/* get para */
	in_port      = 0;/* cfg->disp; */
	in_w         = cfg->width;
	in_h         = cfg->height;
	crop_x       = 0;
	crop_y       = 0;
	crop_w       = cfg->width;
	crop_h       = cfg->height;
	out_addr[0]  = cfg->addr[0];
	out_addr[1]  = cfg->addr[1];
	out_addr[2]  = cfg->addr[2];
	out_buf_w    = cfg->width;
	out_buf_h    = cfg->height;
	out_fmt      = 9;/* DISP_FORMAT_Y;//cfg->format; fixed format */
	out_window_x = 0;
	out_window_y = 0;
	out_window_w = cfg->width;
	out_window_h = cfg->height;

	csc_std = cfg->csc_std;
	self_sync = (out_fmt == 9)?1:0;


	/* (0x30000000|(in_port<<16)|(self_sync == 1)?0x20:0x0); */
	wb_dev[sel]->gctrl.dwval |= 0x20000020;

	wb_dev[sel]->size.dwval = (in_w-1)|((in_h-1)<<16);
	/* input crop window */
	wb_dev[sel]->crop_coord.dwval = crop_x|((crop_y)<<16);
	wb_dev[sel]->crop_size.dwval = (crop_w - 1)|((crop_h - 1)<<16);
	/* output fmt */
	wb_dev[sel]->fmt.dwval = 9;

	if (out_fmt == 9/*DISP_FORMAT_Y*/) {
		out_addr[0] += (out_window_y * out_buf_w + out_window_x);
		out_addr[1] = 0;
		out_addr[2] = 0;
		wb_dev[sel]->wb_addr_a0.dwval = out_addr[0];
		wb_dev[sel]->wb_addr_a1.dwval = 0;
		wb_dev[sel]->wb_addr_a2.dwval = 0;
		wb_dev[sel]->wb_pitch0.dwval = out_buf_w;
		wb_dev[sel]->wb_pitch1.dwval = 0;

	  /* CSC */
		wb_dev[sel]->bypass.dwval |= 0x00000001;
		if (csc_std == 0 || csc_std > 2) {
			wb_dev[sel]->c00.bits.coff = rgb2yuv601[0][0];
			wb_dev[sel]->c01.bits.coff = rgb2yuv601[0][1];
			wb_dev[sel]->c02.bits.coff = rgb2yuv601[0][2];
			wb_dev[sel]->c03.bits.cont = rgb2yuv601[0][3];

			wb_dev[sel]->c10.bits.coff = rgb2yuv601[1][0];
			wb_dev[sel]->c11.bits.coff = rgb2yuv601[1][1];
			wb_dev[sel]->c12.bits.coff = rgb2yuv601[1][2];
			wb_dev[sel]->c13.bits.cont = rgb2yuv601[1][3];

			wb_dev[sel]->c20.bits.coff = rgb2yuv601[2][0];
			wb_dev[sel]->c21.bits.coff = rgb2yuv601[2][1];
			wb_dev[sel]->c22.bits.coff = rgb2yuv601[2][2];
			wb_dev[sel]->c23.bits.cont = rgb2yuv601[2][3];
		} else if (csc_std == 1) {
			wb_dev[sel]->c00.bits.coff = rgb2yuv709[0][0];
			wb_dev[sel]->c01.bits.coff = rgb2yuv709[0][1];
			wb_dev[sel]->c02.bits.coff = rgb2yuv709[0][2];
			wb_dev[sel]->c03.bits.cont = rgb2yuv709[0][3];

			wb_dev[sel]->c10.bits.coff = rgb2yuv709[1][0];
			wb_dev[sel]->c11.bits.coff = rgb2yuv709[1][1];
			wb_dev[sel]->c12.bits.coff = rgb2yuv709[1][2];
			wb_dev[sel]->c13.bits.cont = rgb2yuv709[1][3];

			wb_dev[sel]->c20.bits.coff = rgb2yuv709[2][0];
			wb_dev[sel]->c21.bits.coff = rgb2yuv709[2][1];
			wb_dev[sel]->c22.bits.coff = rgb2yuv709[2][2];
			wb_dev[sel]->c23.bits.cont = rgb2yuv709[2][3];
		} else if (csc_std == 2) {
			wb_dev[sel]->c00.bits.coff = r2gray[0][0];
			wb_dev[sel]->c01.bits.coff = r2gray[0][1];
			wb_dev[sel]->c02.bits.coff = r2gray[0][2];
			wb_dev[sel]->c03.bits.cont = r2gray[0][3];

			wb_dev[sel]->c10.bits.coff = r2gray[1][0];
			wb_dev[sel]->c11.bits.coff = r2gray[1][1];
			wb_dev[sel]->c12.bits.coff = r2gray[1][2];
			wb_dev[sel]->c13.bits.cont = r2gray[1][3];

			wb_dev[sel]->c20.bits.coff = r2gray[2][0];
			wb_dev[sel]->c21.bits.coff = r2gray[2][1];
			wb_dev[sel]->c22.bits.coff = r2gray[2][2];
			wb_dev[sel]->c23.bits.cont = r2gray[2][3];
		}

		/*fine scal*/
		wb_dev[sel]->bypass.dwval &= 0xfffffffb;
		wb_dev[sel]->fs_hstep.dwval = 1 << 20;
		wb_dev[sel]->fs_vstep.dwval = 1 << 20;
		wb_dev[sel]->fs_insize.dwval =
				(out_window_w - 1) | ((out_window_h - 1) << 16);
		wb_dev[sel]->fs_outsize.dwval =
				(out_window_w - 1) | ((out_window_h - 1) << 16);
	}
	return 0;
}


int wb_eink_get_status(unsigned int sel)
{
	unsigned int status;

	status = wb_dev[sel]->status.dwval & 0x71;

	if (status == 0x11)
		return EWB_OK;
	else if (status & 0x20)
		return EWB_OVFL;
	else if (status & 0x40)
		return EWB_TIMEOUT;
	else if (status & 0x100)
		return EWB_BUSY;
	else
		return EWB_ERR;
}

/* /INTERRUPT */
int wb_eink_enableint(unsigned int sel)
{
	/* register irq routine */
	/* os_request_irq(); */

	wb_dev[sel]->intr.dwval |= 0x00000001;
	return 0;
}

int wb_eink_disableint(unsigned int sel)
{
	/* unregister irq routine */

	wb_dev[sel]->intr.dwval &= 0xfffffffe;
	return 0;
}

/* write 1 to clear */
int wb_eink_clearint(unsigned int sel)
{
	wb_dev[sel]->status.dwval |= (WB_END_IE | WB_FINISH_IE |
					WB_FIFO_OVERFLOW_ERROR_IE |
					WB_TIMEOUT_ERROR_IE);
	return 0;
}
#endif
